#! /usr/bin/env perl
##
## Copyright (C) by Argonne National Laboratory
##     See COPYRIGHT in top-level directory
##

# Set Defaults
$debug = 0;
$debugDir = 0;
$gDebugOther = 0;

$summaryOutput = 0;
$terseOutput = 1;
$rootSrcDir = "";
# Define the known commands, along with known make noise (results of
# echo)
# We include /bin/rm and /bin/mv because some Makefile authors prefer
# to get the full path for these routines
@commands = ( "cc", "pgcc", "gcc", "icc", "acc", 
    "rm", "mv", "cp", "ar", "ranlib", "perl", "for", 
    "/bin/rm", "/bin/mv", "/bin/cp", "/bin/chmod",
    "rm", "mv", "cp", "chmod",
    "if", "make", "gnumake", 
    "[A-Za-z0-9_\/\.-]*\/mpicc",
    "[A-Za-z0-9_\/\.-]*\/mpifort",
    "[A-Za-z0-9_\/\.-]*\/mpicxx",
    "cleaning", "sleep", "date", "g77", "f77", "f90", "f95", "pgCC",
    "pgf77", "pgf90", "CC", "g95", "g\\+\\+", "c\\+\\+",
    "acc\\+\\+", "xlc", "xlf", "xlC", "xlf90", "gfortran", 
    "ifort", "ifc", "test",
    "true", "false", "/[A-Za-z0-9_\/\.-]*createshlib", 
    "/bin/sh\\s+[A-Za-z0-9_\/\.-]*libtool\\s+\(--tag=\\w+\)*\\s+--mode=\\w+",
    "/bin/bash\\s+[A-Za-z0-9_\/\.-]*libtool\\s+--mode=\\w+",
    "/bin/sh\\s+[A-Za-z0-9_\/\.-]*libtool\\s+--finish",
    "/bin/bash\\s+[A-Za-z0-9_\/\.-]*libtool\\s+--finish",
    "libtool:\\s+compile:",
    "libtool:\\s+link:",
    "libtool:\\s+install:",
    "libtool:\\s+finish:",
    "[A-Za-z0-9_\/-]*\/icc",
    "[A-Za-z0-9_\/-]*\/install-sh",
    "/usr/bin/ar", "mkdir",
    "/bin/mkdir",
    "/.*bin/mkdir",
    "/usr/ucb/ld",
    "mpicc", "mpicxx",
    "compiling ROMIO in", 
    "Make completed",
    "echo", "cat",
    "NVCC", "CC", "CCLD", "AR", "RANLIB", "LIBTOOL", "MV",
    "MOD", "GEN", "F77", "FC", "CXXLD", "CXX", "FCLD",
    "CP", "LN", # "terse" make versions
    "\\(?cd",
    "\\(\\s*cd",
    "creating\s+lib.*",
    "cd\\s+\&\&\\s+make",
    "sed -e",
    "PATH=\"\\\$PATH.*\\sldconfig\\s",
    "building profiling interface in directory",
    "/usr/bin/sed",
    "/usr/bin/install",
    "/.*bin/install",
    ".*confdb/install-sh",
    "Copying Upshot",
    "Cleaning directory",
    "[*][*][*][*] Making .*\.\.\.\.",
    "Making .* in .*",
    "Making upshot", "\\s*\$",
    "ld: warning: directory not found for option '-L\\S*src/mpl'",
    "ld: warning: directory not found for option '-L\\S*src/openpa/src'",
    "copying selected object files to avoid basename conflicts...",
    "libtool", "Entering directory", "touch", "cd", "config.status:", "\\(CDPATH=.*",
);

# OtherNoise is an array of patterns that describe blocks of
# text that we'd like to skip.  The initial use it to handle 
# libtool install output.
# The PG entries are work-arounds for bugs in the PG header files that have
# small incompatibilities with the system header file /usr/include/unistd.h
#
@OtherNoise = ( 'Libraries have been installed in:<SEP>more information, such as the ld\(1\) and ld.so\(8\) manual pages.<SEP>20',
    '^\s*-+\s*$<SEP><SEP>1', 
    '^In directory:<SEP><SEP>1',
    '^creating\s<SEP><SEP>1', 
    #		'^PGC-W-0114-More than one type.*/usr/include/unistd.h: 189<SEP>^PGC-W-0143-Useless typedef.*/usr/include/unistd.h: 189<SEP>2',
    #		'^PGC-W-0114-More than one type.*/usr/include/unistd.h: 243<SEP>^PGC-W-0143-Useless typedef.*/usr/include/unistd.h: 243<SEP>2',
    '^PGC.*: compilation completed with warnings<SEP><SEP>1',
    # This next is a general version to handle all of these mistakes
    '^PGC-W-0114-More than one type specified.*/usr/include<SEP>^PGC-W-0143-Useless typedef declaration \(no declarators present\).*/usr/include<SEP>2',
    'copying python',
    'LOOP WAS VECTORIZED',
    'Using variables ',
    'remark #\d+: .*', # intel compiler
);
# In the past, we also needed
# WARNING 84.*not used for resolving any symbol
# Info: File not optimized


$trimCompileLine = 1;

# Create the variables that keep track of state
# dirstack keeps the directory name that gnumake and similar make
# implementations echo as a directory is entered or exited (this can generate
# a great deal of output noise that can obscure important data.  However, when
# a problem *does* occur, this directory information is valuable.
# dirprinted is a parallel array that indicates whether the corresponding
# directory entry has been printed.
@dirstack = ();
@dirprinted = ();

# Process arguments to select options
foreach $_ (@ARGV) {
    if (/^--?debug/) { $debug = 1; }
    elsif (/^--?showdir/) { $debugDir = 1; }
    elsif (/^--?summary/) { $summaryOutput = 1; }
    elsif (/^--?notrimcompileline/) { $trimCompileLine = 0; }
    elsif (/^-/) {
        print STDERR "Unrecognized argument $_\n";
        print STDERR "clmake [ -debug ] [ -showdir ] [ -summary ]\n";
        exit(1);
    }
    else { last; }
    shift @ARGV;
}
# Read lines.  Categorize lines as commands and output.  This
# script suppresses commands that generate no extra output.
# The approach is to read a line and then read the next line.
# While the line is a command and the next line is not, output the
# line.
#
# We use eof() (see man perlfun) so that we can accept filenames on the
# commandline.
#
$cmdline = "";
$inside_module = 0;
T:
while ($line = <>) {
    if (eof()) { next; }

    $line =~ s/\r//;    # remove returns from line (de DOSify)

    # Read the next line, including handling any continuation lines
    while ($line =~ /\\$/) {
        print "Read continuation line (cmd) ... \n" if $debug;
        $line .= <>;
    }

    # Check for noise
    foreach my $pat (@OtherNoise) {
        # maxlines is the maximum number of lines to match
        my ($beginpat,$endpat,$maxlines) = split( '<SEP>', $pat );
        print "Trying to match $beginpat\n" if $gDebugOther;
        if ($line =~ /$beginpat/) {
            $maxlines --;
            print "Found match to $beginpat\n" if $gDebugOther;
            # Found the beginning.  Look for the end (if one requested)
            if ($endpat =~ /\S/) {
                print "Trying to match endpat $endpat\n" if $gDebugOther;
                while ($line = <>) {
                    if (eof()) { last; }
                    print "Trying to match to $line" if $gDebugOther;
                    if ($line =~ /$endpat/) { last; }
                    if ($maxlines-- <= 0) { 
                        print STDOUT "Failed to match $endpat; last line was $line";
                        last; 
                    }
                }
            }
            next T;
        }
    }

    # Is the line a command (including a directory change?)
    # Check first for a directory change
    if ($line =~ /^\s*make.* Entering directory \'([^\']*)\'/) {
        my $dirname = $1;
        $dirstack[$#dirstack+1] = $dirname;
        $dirprinted[$#dirprinted+1] = 0;
        print ">>entering $dirname\n" if $debugDir;
        if ($dirname =~ /modules/) {
            $inside_module++;
        }
        $cmdline = "";
        next;
    }
    elsif ($line =~ /^\s*make.* Leaving directory \'([^\']*)\'/) {
        # We should check that the directory names match
        my $dirname = $1;
        print ">>leaving $dirname\n" if $debugDir;
        if ($dirname ne $dirstack[$#dirstack]) {
            print STDERR "Warning: leaving directory $dirname but expected to leave directory " . $dirstack[$#dirstack] . "\n";
        }
        $#dirstack--;
        $#dirprinted--;
        if ($dirname =~ /modules/) {
            $inside_module--;
        }
        $cmdline = "";
        next;
    }
    elsif ($inside_module > 0) {
        # Ignore warnings coming from inside other modules. These are outside of MPICH's control
        next;
    }
    else {
        $is_command = 0;
        foreach $cmdname (@commands) {
            if ($line =~ /^\s*$cmdname\W/) {
                $is_command = 1;
                $cmdline = $line;
                last;
            }
        }
        if ($is_command) { 
            if ($summaryOutput) {
                # FIXME: Summarize the command
                if ($terseOutput) {
                    if ($rootSrcDir eq "") {
                        if ($line =~ /\s(\/\S+\/src\/)/) {
                            $rootSrcDir = $1;
                            print "rootSrcDir = $rootSrcDir\n";
                        }
                    }
                    else {
                        $line =~ s/$rootSrcDir//g;
                    }
                    # Compiler options to remove
                    $line =~ s/-I\S+\s+//g;
                    $line =~ s/-W\S+\s+//g;
                    $line =~ s/-D\S+\s+//g;
                }
                print $line;
            }
            next; 
        }
    }

    # If we got to this point, the line was not a recognized command or
    # ignorable output.  Output the line, including the command if this 
    # is the first time.  We can then forget the command 
    if ($#dirstack >= 0 && $dirprinted[$#dirstack] == 0) {
        print "In directory: ". $dirstack[$#dirstack] . "\n";
        $dirprinted[$#dirstack] = 1;
    }
    if ($cmdline ne "") { 
        if ($trimCompileLine) {
            # Simplify the command line before printing
            $cmdline =~ s/-I\S*\s+//g;
            $cmdline =~ s/-ansi//g;
            $cmdline =~ s/-W\S*\s+//g;
            $cmdline =~ s/-DHAVE_CONFIG_H//g;
            $cmdline =~ s/-DGCC_WALL//g;
        }
        # We could print a newline here to separate commands
        print $cmdline;
        $cmdline = "";
    }
    print $line;
}

#
# ToDo:
# Handle lines containing just ----, e.g., patterns of the form
# -*
